
//#undef UNICODE
//#undef _UNICODE
// cl Planecraft.cpp /utf-8

/*
0.1-alpha
0.2-beta
20240108
1.0(1) 飞机, Enemy0-2, bullet, meteorite
1.1(1) pause
1.1(2) fix.
1.2(3) remove console.
1.3(5) add Item, buff
2024-01-09
1.4(9) 加入袭击事件,陨石伤害改为tick*dmg
20240110
1.5 add carrier, debuff, carrier summon enemy
1.6 add Introduce
1.6.1 减慢Carrier的射击速度, 调整item概率和风暴变化速度
*/

#include <seg.hpp>
#include "choice.h"
#pragma comment(lib, "choice.lib")
#define _ver1 L"1.6.1"
#define and &&
#define is ==
#define or ||
#define elif else if
//#include <seobjects.hpp>

typedef int tick_t;
typedef long ADDON;
constexpr int Width = 1660;
constexpr int Height= 930;
constexpr int EnemyMax = 12;
constexpr int BulletMax= 69;
constexpr int StoneMax = 8;
constexpr int ItemMax = 9;
constexpr int MagicMax = 6;
constexpr int PlaneWeight = 42;// length & width
constexpr int BulletWeight= 4;
constexpr int ItemWeight = 12;
constexpr int MagicWeight = 8;
constexpr int BulletSpeed = 8;// 8
constexpr int tps = 20;
constexpr int tpswait = 1000 / tps;
constexpr int PlayerSpeed = 10;
constexpr int EnemySpeed = 7;
constexpr int ItemSpeed = 7;
constexpr int MagicSpeed = 6;
constexpr int PlayerAtkCool = 4;
constexpr int EnemyAtkCool = 18;
constexpr int EnemyAttacksPer = 4; // 袭击概率, 出现大量敌对实体
constexpr int BossRateForWinner = 68;
constexpr float CarrierHPMax = 20000;
constexpr int CarrierRegeneratePer = 12;
constexpr int CarrierAtkcoolMax = 22;
constexpr int CarrierTrySummonEnemyPer = 5;
constexpr bool CanMeteoriteDestroyBullet = true;
constexpr float BulletDamageMain = 200;
constexpr float MeteoriteDamageBase = 20; // this * radius = damae
constexpr float EnemyBombBlastRange = (float)PlaneWeight * 2 + 100;
constexpr float EnemyBombBlastDamageBase = 160*3.2; // this + Euladis * 2 # this = dmg

constexpr tick_t get_tps(const int ms, const tick_t tps_ = tps)
{
    return ms / tps_;
}

enum Direct {
    D_LEFT, D_RIGHT, D_DOWN
};
enum Enemy_Type {
    ET_NORMAL, ET_BOSS, ET_BOMB, ET_CARRIER
};
enum Entitybullet_Belong {
    EB_PLAYER, EB_ENEMY
};
enum Move_Method {
    MM_XLINE, MM_YLINE, MM_FLOAT
};
enum Item_Type {
    IT_POWER, IT_AID, IT_SHIELD
};
enum MagicBullet_Type {
    MT_FIRE, MT_BLIND
};
int ItemSelectWeight[3] = {20, 32, 12};
int MagicSelectWeight[2] = {60, 37};
constexpr tick_t PowerTickMax = 20 * 8;
constexpr tick_t AidTickMax = 20 * 18;
constexpr tick_t FireTickMax = 20 * 5; // hp-4, shield-1
constexpr tick_t BlindTickMax = 20 * 4;
constexpr float ShieldReduceDamagePer = 0.95; // dmg = dmg * (1 - this * (1000.0f-ShieldHP))
constexpr int8_t GetItemPer = 80; // item爆率

typedef int8_t DIRECT;

typedef struct __Plane__
{
    int x;
    int y;
    float hp;
    COLORREF color;
}__Plane;

typedef struct __Player
{
    __Plane base;
    int8_t atkcool;
    float ShieldHP;
    tick_t Aid;
    tick_t Multishot;
    tick_t Fire; // debuff
    tick_t Blind; // debuff
}Player;

typedef struct __Enemy
{
    __Plane base;
    bool liv;
    DIRECT d;
    int8_t dtype;
    int8_t atkcool;
    ADDON addon;
}Enemy;


typedef struct __Bullet
{
    int x;
    int y;
    float damage;
    int dx;
    int dy;
    bool liv;
    int8_t belong;
    COLORREF col;
}Bullet;

typedef struct
{
    int x;
    int y;
    float radius;
    bool liv;
    int8_t movem;
    int_fast8_t yspeed; // 4 ~ 10
    int8_t xspeed; // MM_FLOAT only
}Meteorite;

typedef struct __Item
{
    int x;
    int y;
    //int speed; == enemyspeed
    int8_t dtype;
    bool liv;
}Item;

typedef struct
{
    int x;
    int y;
    COLORREF col;
    int8_t dtype;
    bool liv;
}Magic;


struct
{
    long Shoot;
    long onTarget;
    int q;
    double behit;
    long score;
}StatisticalData{0, 0, 0, 0.0, 0};

wchar_t ss[280];

Player pl;
Enemy EnemyList[EnemyMax];
Bullet BulletList[BulletMax];
Meteorite StoneList[StoneMax];
Item ItemList[ItemMax];
Magic MagicList[MagicMax];

// runtime
int EnemyTypeWeight[3] = {10, 1, 1};
int windy = 1; // wind move stone
int score = 0;
Enemy *CarrierBossBarMain = nullptr;
int Enemy_Count = 0;
int Stone_Count = 0;
int BulletCount = 0;
int Items_Count = 0;
int Magic_Count = 0;

void initplayer();
void initenemies();
void initbullets();
void initstones();
void inititems();
void initmagics();

void CreateMeteorite();
void SummonEnemy();
void CreateBullet(Player &self);
void CreateBullet(Enemy & self);
void CreateItem(Enemy &self); // 爆金币
void CreateMagic(Enemy &self);
void SummonCarrier();

void mov(Player &self);
void mov(Enemy &self);
void mov(Bullet &self);
void mov(Meteorite &self);
void mov(Item &self);
void mov(Magic &self);

void shoot(Player &self);
void shoot(Enemy &self);
void hit(Player &self, float dmg);
void hit(Enemy &self, float dmg, int8_t by);
void fire(Player &self);

void render(Player &self);
void render(Enemy &self);
void render(Bullet &self);
void render(Meteorite &self);
void render(Item &self);
void render(Magic &self);

void draw_bar(Enemy *carrier);

void Introduce();
/*
bool errflg = false;
void printm(const wchar_t *s)
{
    time_t Now;
    time(&Now);
    tm *p = localtime(&Now);
    wprintf(L"[%2d:%2d:%2d] %s\n", p->tm_hour, p->tm_min, p->tm_sec, s);
}
HANDLE stdouthdl;
void cerr(const wchar_t *s, WORD col = FOREGROUND_INTENSITY | FOREGROUND_RED)
{
    SetConsoleTextAttribute(stdouthdl, col);
    printm(s);
    SetConsoleTextAttribute(stdouthdl,FOREGROUND_INTENSITY|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED);
}
*/
// eular distance(line)
float EulaDis(int x1, int y1,   int x2, int y2)
{
    long f = (x1 - x2) * (x1 - x2)  +  (y1 - y2) * (y1 - y2);
    return sqrtf(f);
}
bool prob(int p)
{
    return p > seg::Math::randint(0, 100);
}

void StartNormal()
{
    initplayer(); initenemies(); initbullets(); initstones(); inititems(); initmagics();
    EnemyTypeWeight[0] = 30; // normal
    EnemyTypeWeight[1] = 4;  // boss
    EnemyTypeWeight[2] = 10; // bomb
    Enemy_Count = 0;
    Stone_Count = 0;
    BulletCount = 0;
    Items_Count = 0;
    score = 0;
    settextcolor(RGB(255, 160, 255));
    if (prob(50))
    {
        windy = seg::Math::randint(1, 3);
    }
    else
    {
        windy = -seg::Math::randint(1, 3);
    }
    bool SummonCarrier_1 = true;
    constexpr int AddCarrierTime_1 = (float)BossRateForWinner * 0.72;
    seg::Times::Clock FPS;
    FPS.init();
    FPS.tick(6000);
    auto addTime = seg::Times::get_ticks(); // + 
    auto EntityTps = addTime;
    auto TickNow = EntityTps;
    auto AddEnemyTime = TickNow;
    auto AdjustETW = TickNow;
    auto Bullet_Tps = TickNow;
    while (true)
    {
        FPS.tick();
        TickNow = seg::Times::get_ticks();
        if (TickNow >= addTime)
        {
            addTime = TickNow + seg::Math::randint(1600, 12600);
            CreateMeteorite();
            if (prob(18))
            {
                if (prob(50))
                {
                    windy = seg::Math::randint(1, 3);
                }
                else
                {
                    windy = -seg::Math::randint(1, 3);
                }
            }
        }
        if (TickNow >= AddEnemyTime and (EnemyTypeWeight[1] <= BossRateForWinner))
        {
            AddEnemyTime = TickNow + seg::Math::randint(1630, 5260);
            SummonEnemy();
            if (Enemy_Count <= 0)
            {
                SummonEnemy();
            }
            if (SummonCarrier_1 and EnemyTypeWeight[1] is AddCarrierTime_1)
            {
                SummonCarrier_1 = false;
                SummonCarrier();
            }
        }
        if (TickNow >= AdjustETW)
        {
            AdjustETW = TickNow + 20'000;
            EnemyTypeWeight[1] ++;
        }
        if (TickNow >= EntityTps)
        {
            EntityTps = TickNow + tpswait;
            if (prob(30))
            {
                pl.base.hp += 1;
                if (pl.base.hp > 1000)
                {
                    pl.base.hp = 1000.0f;
                }
            }
            mov(pl);
            for (int i = 0; i < EnemyMax; i++)
            {
                if (EnemyList[i].liv)
                {
                    mov(EnemyList[i]);
                }
            }
            for (int i = 0; i < StoneMax; i++)
            {
                if (StoneList[i].liv)
                {
                    mov(StoneList[i]);
                }
            }
            for (int i = 0; i < ItemMax; i++)
            {
                if (ItemList[i].liv)
                {
                    mov(ItemList[i]);
                }
            }
            for (int i = 0; i < MagicMax; ++i)
            {
                if (MagicList[i].liv)
                {
                    mov(MagicList[i]);
                }
            }
        }
        if (TickNow >= Bullet_Tps)
        {
            Bullet_Tps = TickNow + 10;
            for (int i = 0; i < BulletMax; i++)
            {
                if (BulletList[i].liv)
                {
                    mov(BulletList[i]);
                }
            }
        }
        // render
        if (pl.Blind <= 0)
        {
            seg::render::fill(RGB(4, 6, 16));
            for (int i = 0; i < StoneMax; i++)
            {
                if (StoneList[i].liv)
                {
                    render(StoneList[i]);
                }
            }
            for (int i = 0; i < EnemyMax; i++)
            {
                if (EnemyList[i].liv)
                {
                    render(EnemyList[i]);
                }
            }
            for (int i = 0; i < ItemMax; i++)
            {
                if (ItemList[i].liv)
                {
                    render(ItemList[i]);
                }
            }
        }
        else
        {
            seg::render::fill(RGB(255, 255, 255));
        }
        for (int i = 0; i < BulletMax; i++)
        {
            if (BulletList[i].liv)
            {
                render(BulletList[i]);
            }
        }
        for (int i = 0; i < MagicMax; i++)
        {
            if (MagicList[i].liv)
            {
                render(MagicList[i]);
            }
        }
        render(pl);
        draw_bar(CarrierBossBarMain);
        swprintf(ss, L"FPS:%d/%d Windy:%d BR: %d/%d", FPS.get_fps(), FPS.ask_fpsmin(), windy, EnemyTypeWeight[1], BossRateForWinner);// boss rate
        outtextxy(0, 0, (LPCTSTR)(ss));
        swprintf(ss, L"E:%d, B:%d, S:%d, I:%d, M:%d", Enemy_Count, BulletCount, Stone_Count, Items_Count, Magic_Count);
        outtextxy(0, 16, (LPCTSTR)ss);
        // right top
        swprintf(ss, L"Position: (%d, %d), Score:%d", pl.base.x, pl.base.y, score);
        seg::render::text(Width-1, 0, ss, seg::render::Place::righttop);
        swprintf(ss, L"HP:%.2f, Power:%d, Aid:%d, Shield:%.1f", pl.base.hp, pl.Multishot,pl.Aid,pl.ShieldHP);
        seg::render::text(Width-1, 16, ss, seg::render::Place::righttop);
        swprintf(ss, L"Fire:%d, Blind:%d", pl.Fire, pl.Blind);
        seg::render::text(Width-1, 32, ss, seg::render::Place::righttop);
        seg::render::flip();
        if (pl.base.hp <= 0)
        {
            //printm(L"Player lose.");
            //Sleep(3000);
            while (true)
            {
                Sleep(50);
                swprintf(ss, L"You lose. Score is: %d", score);
                seg::render::text(Width/2, Height/2-10, ss, seg::render::Place::center);
                swprintf(ss, L"Press ENTER to quit.");
                seg::render::text(Width/2, Height/2+10, ss, seg::render::Place::center);
                seg::render::flip();
                if (seg::Key::GetVirtualKey(VK_RETURN))
                {
                    break;
                }
            }
            StatisticalData.score += score;
            return;
        }
        if (EnemyTypeWeight[1] > BossRateForWinner and CarrierBossBarMain != nullptr) {
            if (CarrierBossBarMain->liv is false)
            {
                //printm(L"Player win!");
                while (true)
                {
                    Sleep(50);
                    swprintf(ss, L"You win! Score is: %d", score);
                    seg::render::text(Width/2, Height/2-10, ss, seg::render::Place::center);
                    swprintf(ss, L"Press ENTER to quit.");
                    seg::render::text(Width/2, Height/2+10, ss, seg::render::Place::center);
                    seg::render::flip();
                    if (seg::Key::GetVirtualKey(VK_RETURN))
                    {
                        break;
                    }
                }
                StatisticalData.score += score;
                return;
            }
        }
        if (seg::Key::GetVirtualKey('P'))
        {
            while (true)
            {
                swprintf(ss, L"已暂停, 按ESC继续");
                seg::render::text(Width/2, Height /2, (TCHAR*)ss, seg::render::Place::center);
                seg::render::flip();
                Sleep(50);
                if (seg::Key::GetVirtualKey(VK_ESCAPE))
                {
                    break;
                }
            }
        }
    }
}

//int __stdcall wWinMain(HINSTANCE__ *hInstance, HINSTANCE__ *hPrevInstance, LPWSTR lpCmdLine, int nshow)
//{
//    seg::EasyX_Initialize()
//}

//int wmain(int argc, wchar_t *argv)
int __stdcall wWinMain(HINSTANCE__ *hInstance, HINSTANCE__ *hPrevInstance, LPWSTR lpCmdLine, int nshow)
{
    //printm(L"Initialize Planecraft " _ver1);
    seg::EasyX_Initialize(Width, Height, L"Planecraft " _ver1 );//EX_SHOWCONSOLE);
    SetWindowPos(seg::MainHandle, NULL, 10, 10, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
    seg::render::fill(RGB(30, 50, 69));
    settextcolor(RGB(255, 196, 20));
    seg::render::text(Width/2, Height/2, L"Press 'S' to start.", seg::render::Place::center);
    //stdouthdl = GetStdHandle(STD_OUTPUT_HANDLE);
    seg::render::flip();
    //printm(L"Enter mainloop.");
    while (true)
    {
        Sleep(100);
        seg::render::fill(RGB(30, 50, 69));
        seg::render::text(Width/2, Height/2-10, L"Press 'S' to start, 'I' for help.", seg::render::Place::center);
        swprintf(
            ss, L"Shoot: %ld,  On Target: %ld,  Q: %d,  Score: %ld,  Behit: %.3lf",
            StatisticalData.Shoot,StatisticalData.onTarget,
            StatisticalData.q, StatisticalData.score, StatisticalData.behit
        );
        seg::render::text(Width/2, Height/2+10, (TCHAR*)ss, seg::render::Place::center);
        seg::render::flip();
        if (seg::Key::GetVirtualKey('S'))
        {
            StartNormal();
        }
        elif (seg::Key::GetVirtualKey('I'))
        {
            Introduce();
        }
    }
}


void initplayer()
{
    pl.base.hp = 1000.0;
    pl.base.x = Width/2;
    pl.base.y = Height - PlaneWeight - 1;
    pl.base.color = RGB(0, 255, 255);
    pl.atkcool = 0;
}

void initenemies()
{
    for (int i = 0; i < EnemyMax; i++)
    {
        EnemyList[i].liv = false;
    }
    Enemy_Count = 0;
}

void initbullets()
{
    for (int i = 0; i < BulletMax; i++)
    {
        BulletList[i].liv = false;
    }
    BulletCount = 0;
}

void initstones()
{
    for (int i = 0; i < StoneMax; i++)
    {
        StoneList[i].liv = false;
    }
    Stone_Count = 0;
}

void inititems()
{
    for (int i = 0; i < ItemMax; i++)
    {
        ItemList[i].liv = false;
    }
    Items_Count = 0;
}

void initmagics()
{
    for (int i = 0; i < MagicMax; i++)
    {
        MagicList[i].liv = false;
    }
    Magic_Count = 0;
}

void __CreateStone()
{
    for (int i = 0; i < StoneMax; i++)
    {
        if (StoneList[i].liv == false)
        {
            StoneList[i].liv = true;
            StoneList[i].x = seg::Math::randint(100, Width-100);
            StoneList[i].y = 100;
            if (prob(50))
            {
                StoneList[i].movem = MM_YLINE;
                StoneList[i].radius = seg::Math::randint(28, 180);
                StoneList[i].yspeed = seg::Math::randint(4, 8);
            }
            else
            {
                StoneList[i].movem = MM_FLOAT;
                StoneList[i].radius = seg::Math::randint(18, 56);
                StoneList[i].yspeed = seg::Math::randint(6, 10);
                if (prob(50))
                {
                    StoneList[i].xspeed = seg::Math::randint(0, 8);
                } else {
                    StoneList[i].xspeed = -seg::Math::randint(0, 7);
                }
            }
            Stone_Count ++;
            return;
        }
    }
    
}
void CreateMeteorite()
{
    int i = seg::Math::randint(0, 4);
    if (i == 0) {return;}
    for (int j = 0; j < i; j++)
    {
        __CreateStone();
    }
}

void __SummonEnemy()
{
    static int _[3] = {10, 10, 4};
    for (int i = 0; i < EnemyMax; i++)
    {
        if (EnemyList[i].liv == false)
        {
            EnemyList[i].liv = true;
            EnemyList[i].d = WeightChoice(_, 3);
            EnemyList[i].dtype = WeightChoice(EnemyTypeWeight, 3);
            EnemyList[i].base.y = PlaneWeight+1;
            EnemyList[i].base.x = seg::Math::randint(PlaneWeight, Width - PlaneWeight);
            EnemyList[i].base.hp= 1000;
            switch (EnemyList[i].dtype)
            {
            case ET_NORMAL:
            {
                EnemyList[i].base.color = RGB(255, 255, 8);
            }
                break;
            case ET_BOSS:
            {
                EnemyList[i].base.color = RGB(0xb3, 0x0e, 0xdc);
            } break;
            
            case ET_BOMB:
            {
                EnemyList[i].base.color = RGB(255, 32, 0);
                EnemyList[i].addon = -10;
            } break;
            
            default:
                //errflg = true;
                //cerr(L"Error in summon enemy: Unknown dtype.");
                break;
            }
            Enemy_Count++;
            return;
            break;
        }
    }
    
}
void SummonEnemy()
{
    if (prob(EnemyAttacksPer))
    {
        int i = seg::Math::randint(4, 9);
        for (int j = 0; j < i; j++)
        {
            __SummonEnemy();
        }
    }
    else
    {
        int i = seg::Math::randint(0, 2);
        if (i == 0) {return;}
        for (int j = 0; j < i; j++)
        {
            __SummonEnemy();
        }
    }
}


void __Player_Multishot(Player &self)
{
    int i;
    for (i = 0; i < BulletMax; i++)
    {
        if (BulletList[i].liv == false)
        {
            BulletList[i].liv = true;
            BulletList[i].x = self.base.x;
            BulletList[i].y = self.base.y - PlaneWeight - 5;
            BulletList[i].damage = BulletDamageMain / 2;
            BulletList[i].belong = EB_PLAYER;
            BulletList[i].dx = BulletSpeed / 3;
            BulletList[i].dy = -BulletSpeed;
            BulletList[i].col = RGB(0xc5, 0xfb, 0x46) ;
            BulletCount++;
            break;
        }
    }
    for (i ; i < BulletMax; i++)
    {
        if (BulletList[i].liv == false)
        {
            BulletList[i].liv = true;
            BulletList[i].x = self.base.x;
            BulletList[i].y = self.base.y - PlaneWeight - 5;
            BulletList[i].damage = BulletDamageMain / 3;
            BulletList[i].belong = EB_PLAYER;
            BulletList[i].dx = BulletSpeed / 2;
            BulletList[i].dy = -BulletSpeed;
            BulletList[i].col = RGB(0xc5, 0xfb, 0x46) ;
            BulletCount++;
            break;
        }
    }
    for (i ; i < BulletMax; i++)
    {
        if (BulletList[i].liv == false)
        {
            BulletList[i].liv = true;
            BulletList[i].x = self.base.x;
            BulletList[i].y = self.base.y - PlaneWeight - 5;
            BulletList[i].damage = BulletDamageMain / 2;
            BulletList[i].belong = EB_PLAYER;
            BulletList[i].dx = -(BulletSpeed / 3);
            BulletList[i].dy = -BulletSpeed;
            BulletList[i].col = RGB(0xc5, 0xfb, 0x46) ;
            BulletCount++;
            break;
        }
    }
    for (i ; i < BulletMax; i++)
    {
        if (BulletList[i].liv == false)
        {
            BulletList[i].liv = true;
            BulletList[i].x = self.base.x;
            BulletList[i].y = self.base.y - PlaneWeight - 5;
            BulletList[i].damage = BulletDamageMain / 3;
            BulletList[i].belong = EB_PLAYER;
            BulletList[i].dx = -(BulletSpeed / 2);
            BulletList[i].dy = -BulletSpeed;
            BulletList[i].col = RGB(0xc5, 0xfb, 0x46) ;
            BulletCount++;
            break;
        }
    }
}
void CreateBullet(Player &self)
{
    for (int i = 0; i < BulletMax; i++)
    {
        if (BulletList[i].liv == false)
        {
            BulletList[i].liv = true;
            BulletList[i].x = self.base.x;
            BulletList[i].y = self.base.y - PlaneWeight;
            BulletList[i].damage = BulletDamageMain;
            BulletList[i].belong = EB_PLAYER;
            BulletList[i].dx = 0;
            BulletList[i].dy = -BulletSpeed;
            BulletList[i].col = RGB(255, 0, 255) ;
            BulletCount ++;
            break;
        }
    }
    if (self.Multishot > 0)
    {
        __Player_Multishot(self);
    }
}

// main
void __CreateBullet1(Enemy &self)
{
    for (int i = 0; i < BulletMax; i++)
    {
        if (BulletList[i].liv == false)
        {
            BulletList[i].liv = true;
            BulletList[i].x = self.base.x;
            BulletList[i].y = self.base.y + PlaneWeight + 5;
            BulletList[i].damage = BulletDamageMain;
            BulletList[i].belong = EB_ENEMY;
            BulletList[i].dx = 0;
            BulletList[i].dy = BulletSpeed;
            BulletList[i].col = RGB(255, 0, 255) ;
            BulletCount++;
            break;
        }
    }
}
// added
void __CreateBullet2(Enemy &self)
{
    int i;
    for (i = 0; i < BulletMax; i++)
    {
        if (BulletList[i].liv == false)
        {
            BulletList[i].liv = true;
            BulletList[i].x = self.base.x;
            BulletList[i].y = self.base.y + PlaneWeight + 5;
            BulletList[i].damage = BulletDamageMain / 6;
            BulletList[i].belong = EB_ENEMY;
            BulletList[i].dx = BulletSpeed / 3;
            BulletList[i].dy = BulletSpeed;
            BulletList[i].col = RGB(0xc5, 0xfb, 0x46) ;
            BulletCount++;
            break;
        }
    }
    for (i ; i < BulletMax; i++)
    {
        if (BulletList[i].liv == false)
        {
            BulletList[i].liv = true;
            BulletList[i].x = self.base.x;
            BulletList[i].y = self.base.y + PlaneWeight + 5;
            BulletList[i].damage = BulletDamageMain / 8;
            BulletList[i].belong = EB_ENEMY;
            BulletList[i].dx = BulletSpeed / 2;
            BulletList[i].dy = BulletSpeed;
            BulletList[i].col = RGB(0xc5, 0xfb, 0x66) ;
            BulletCount++;
            break;
        }
    }
    for (i ; i < BulletMax; i++)
    {
        if (BulletList[i].liv == false)
        {
            BulletList[i].liv = true;
            BulletList[i].x = self.base.x;
            BulletList[i].y = self.base.y + PlaneWeight + 5;
            BulletList[i].damage = BulletDamageMain / 6;
            BulletList[i].belong = EB_ENEMY;
            BulletList[i].dx = -(BulletSpeed / 3);
            BulletList[i].dy = BulletSpeed;
            BulletList[i].col = RGB(0xc5, 0xfb, 0x46) ;
            BulletCount++;
            break;
        }
    }
    for (i ; i < BulletMax; i++)
    {
        if (BulletList[i].liv == false)
        {
            BulletList[i].liv = true;
            BulletList[i].x = self.base.x;
            BulletList[i].y = self.base.y + PlaneWeight + 5;
            BulletList[i].damage = BulletDamageMain / 8;
            BulletList[i].belong = EB_ENEMY;
            BulletList[i].dx = -(BulletSpeed / 2);
            BulletList[i].dy = BulletSpeed;
            BulletList[i].col = RGB(0xc5, 0xfb, 0x66) ;
            BulletCount++;
            break;
        }
    }
}
void CreateBullet(Enemy &self)
{
    switch (self.dtype)
    {
    case ET_NORMAL:
    {
        __CreateBullet1(self);
    }
        break;
    
    case ET_BOSS:
    {
        __CreateBullet1(self);
        __CreateBullet2(self);
    } break;
    
    default:
        break;
    }
}

void CreateItem(Enemy &self)
{
    for (int i = 0; i < ItemMax; i++)
    {
        if (ItemList[i].liv is false)
        {
            ItemList[i].liv = true;
            ItemList[i].x = self.base.x;
            ItemList[i].y = self.base.y;
            ItemList[i].dtype = WeightChoice(ItemSelectWeight, 3);
            Items_Count++;
            return;
            break;
        }
    }
}

void CreateMagic(Enemy &self)
{
    for (int i = 0; i < MagicMax; i++)
    {
        if (MagicList[i].liv is false)
        {
            MagicList[i].liv = true;
            MagicList[i].x = self.base.x;
            MagicList[i].y = self.base.y + PlaneWeight + 10;
            MagicList[i].dtype = WeightChoice(MagicSelectWeight, 2);
            switch (MagicList[i].dtype)
            {
            case MT_FIRE:
            {
                MagicList[i].col = RGB(255, 255, 0);
            }
                break;
            
            case MT_BLIND:
            {
                MagicList[i].col = RGB(255, 255, 255);
            } break;
            
            default:
                break;
            }
            Magic_Count++;
            return;
        }
    }
    
}

void SummonCarrier()
{
    static int _[2] = {10, 10};
    for (int i = 0; i < EnemyMax; i++)
    {
        if (EnemyList[i].liv == false)
        {
            EnemyList[i].liv = true;
            EnemyList[i].d = WeightChoice(_, 2);
            EnemyList[i].dtype = ET_CARRIER;
            EnemyList[i].base.y = PlaneWeight+1;
            EnemyList[i].base.x = seg::Math::randint(PlaneWeight, Width - PlaneWeight);
            EnemyList[i].base.hp= CarrierHPMax;
            EnemyList[i].base.color = RGB(0,132,132);
            Enemy_Count++;
            CarrierBossBarMain = &(EnemyList[i]);
            return;
            break;
        }
    }
}

void mov(Player &self)
{
    if (self.atkcool > 0)
    {
        self.atkcool --;
    }
    if (seg::Key::GetVirtualKey('A'))
    {
        self.base.x -= PlayerSpeed;
    } if (seg::Key::GetVirtualKey('D'))
    {
        self.base.x += PlayerSpeed;
    }
    if (seg::Key::GetVirtualKey('J') and self.atkcool <= 0)
    {
        shoot(self);
    }
    if (pl.base.x < 0)
    {
        pl.base.x = 0;
    } else if (pl.base.x >= Width)
    {
        pl.base.x = Width;
    }
    // getitem
    if (self.Aid > 0)
    {
        self.Aid--;
        if (prob(82))
        {
            self.base.hp += 1;
            if (self.base.hp > 1000)
            {
                self.base.hp = 1000.0f;
            }
        }
    }
    if (self.Multishot > 0)
    {
        self.Multishot--;
    }
    if (self.Fire > 0)
    {
        self.Fire--;
        fire(self);
    }
    if (self.Blind > 0)
    {
        self.Blind --;
    }
    for (int i = 0; i < ItemMax; i++)
    {
        if (ItemList[i].liv)
        {
            if (EulaDis(self.base.x, self.base.y, ItemList[i].x, ItemList[i].y) < (PlaneWeight+ItemWeight))
            {
                ItemList[i].liv = false;
                Items_Count--;
                switch (ItemList[i].dtype)
                {
                case IT_POWER:
                {
                    self.Multishot = PowerTickMax;
                }
                    break;
                
                case IT_AID:
                {
                    self.Aid = AidTickMax;
                } break;
                
                case IT_SHIELD:
                {
                    self.ShieldHP = 1000;
                } break;
                
                default:
                    break;
                }
            }
        }
    }
    
}

void __mov_EnemyBomb(Enemy &self)
{
    if (self.addon > 0)
    {
        // had blast
        self.addon --;
    }
    else if (self.addon > -9)
    {
        // clean
        self.liv = false;
        Enemy_Count--;
    }
    else
    {
        // await
        self.base.y += EnemySpeed;
        if (self.base.y >= Height)
        {
            self.liv = false;
            Enemy_Count--;
        }
        else
        {
            float dis = EulaDis(self.base.x, self.base.y, pl.base.x, pl.base.y);
            if (dis < EnemyBombBlastRange)
            {
                // bbb
                self.addon = (ADDON)get_tps(1600, 20);
                hit(pl, EnemyBombBlastDamageBase);
            }
        }
    }
}
void __Summon_DesignatedEnemy(int8_t Type_, int _x, int _y, DIRECT _d = -1)
{
    static int _[3] = {10, 10, 4};
    for (int i = 0; i < EnemyMax; i++)
    {
        if (EnemyList[i].liv == false)
        {
            EnemyList[i].liv = true;
            if (_d < 0 or _d > D_DOWN)
                EnemyList[i].d = WeightChoice(_, 3);
            else
                EnemyList[i].d = _d;
            EnemyList[i].dtype = Type_;
            EnemyList[i].base.y = _y;
            EnemyList[i].base.x = _x;
            EnemyList[i].base.hp= 1000;
            switch (EnemyList[i].dtype)
            {
            case ET_NORMAL:
            {
                EnemyList[i].base.color = RGB(255, 255, 8);
            }
                break;
            case ET_BOSS:
            {
                EnemyList[i].base.color = RGB(0xb3, 0x0e, 0xdc);
            } break;
            
            case ET_BOMB:
            {
                EnemyList[i].base.color = RGB(255, 32, 0);
                EnemyList[i].addon = -10;
            } break;
            
            default:
                //errflg = true;
                //cerr(L"Error in summon enemy: Unknown dtype.");
                break;
            }
            Enemy_Count++;
            return;
            break;
        }
    }
    
}
void __mov_EnemyCarrier(Enemy &self)
{
    if (self.atkcool > 0)
    {
        self.atkcool --;
    }
    else
    {
        CreateMagic(self);
        self.atkcool = CarrierAtkcoolMax;
    }
    if (prob(CarrierRegeneratePer))
    {
        if (self.base.hp < CarrierHPMax)
        {
            self.base.hp += 1;
        }
    }
    if (prob(CarrierTrySummonEnemyPer))
    {
        if (prob(70))
        {
            __Summon_DesignatedEnemy(ET_BOMB, self.base.x, self.base.y, D_DOWN);
        } else {
            __Summon_DesignatedEnemy(ET_BOSS, self.base.x, self.base.y, D_DOWN);
        }
    }
    switch (self.d)
    {
    case D_LEFT:
    {
        self.base.x -= EnemySpeed;
        if (self.base.x < PlaneWeight)
        {
            if (true)
            {
                self.d = D_RIGHT;
                self.base.x += 2;
            }
        }
        else if (self.base.x < 0)
        {
            self.liv = false;
            Enemy_Count--;
        }
    }
        break;
    
    case D_RIGHT:
    {
        self.base.x += EnemySpeed;
        if (self.base.x > (Width - PlaneWeight))
        {
            if (true)
            {
                self.d = D_LEFT;
                self.base.x -= 2;
            }
        }
        else if (self.base.x >= Width)
        {
            self.liv = false;
            Enemy_Count--;
        }
    } break;
    
    case D_DOWN:
    {
        pass
    } break;
    
    default:
        break;
    }
}
void mov(Enemy & self)
{
    if (self.dtype is ET_BOMB)
    {
        __mov_EnemyBomb(self);
        return;
    }
    elif (self.dtype  is  ET_CARRIER)
    {
        __mov_EnemyCarrier(self);
        return;
    }
    if (self.atkcool > 0)
    {
        self.atkcool --;
    }
    else
    {
        shoot(self);
    }
    switch (self.d)
    {
    case D_LEFT:
    {
        self.base.x -= EnemySpeed;
        if (self.base.x < PlaneWeight)
        {
            if (prob(69))
            {
                self.d = D_RIGHT;
                self.base.x += 2;
            }
        }
        else if (self.base.x < 0)
        {
            self.liv = false;
            Enemy_Count--;
        }
    }
        break;
    
    case D_RIGHT:
    {
        self.base.x += EnemySpeed;
        if (self.base.x > (Width - PlaneWeight))
        {
            if (prob(69))
            {
                self.d = D_LEFT;
                self.base.x -= 2;
            }
        }
        else if (self.base.x >= Width)
        {
            self.liv = false;
            Enemy_Count--;
        }
    } break;
    
    case D_DOWN:
    {
        self.base.y += EnemySpeed;
        if (self.base.y >= Height)
        {
            self.liv = false;
            Enemy_Count--;
        }
    } break;
    
    default:
        break;
    }
}

void mov(Bullet &self)
{
    self.x += self.dx;
    self.y += self.dy;
    if (true) // (self.belong is EB_PLAYER)
    {
        for (int i = 0; i < EnemyMax; i++)
        {
            if (EnemyList[i].liv == true)
            {
                if (EulaDis(self.x, self.y, EnemyList[i].base.x, EnemyList[i].base.y) < (PlaneWeight + BulletWeight))
                {
                    hit(EnemyList[i], self.damage, self.belong);
                    self.liv = false;
                    BulletCount--;
                    if (self.belong is EB_PLAYER)
                    {
                        StatisticalData.onTarget++;
                    }
                    return;
                }
            }
        }
        if (EulaDis(self.x, self.y, pl.base.x, pl.base.y) < (PlaneWeight + BulletWeight))
        {
            if (self.belong != EB_PLAYER)
            {
                hit(pl, self.damage);
                self.liv = false;
                BulletCount--;
                return;
            }
        }
    }
    if (self.x >= Width or self.x < 0 or self.y >= Height or self.y < 0)
    {
        self.liv = false;
        BulletCount--;
        return;
    }
}

void mov(Meteorite &self)
{
    switch (self.movem)
    {
    case MM_YLINE:
    {
        self.y += self.yspeed;
    }
        break;
    
    case MM_FLOAT:
    {
        self.y += self.yspeed;
        self.x += self.xspeed;
        if (prob(30))
        {
            self.xspeed += windy;
        }
    }
    default:
        break;
    }
    
    if (self.x < 0 or self.x >= Width or self.y < 0 or self.y >= Height)
    {
        self.liv = false;
        Stone_Count--;
        return;
    }
    if (EulaDis(self.x, self.y, pl.base.x, pl.base.y) < (PlaneWeight + self.radius))
    {
        hit(pl, MeteoriteDamageBase);
    }
    for (int i = 0; i < EnemyMax; i++)
    {
        if (EnemyList[i].liv is true)
        {
            if (EulaDis(self.x, self.y, EnemyList[i].base.x, EnemyList[i].base.y) < (PlaneWeight + self.radius))
            {
                hit(EnemyList[i], MeteoriteDamageBase, -1);
            }
        }
    }
    if (CanMeteoriteDestroyBullet is true)
    {
        for (int i = 0; i < BulletMax; i++)
        {
            if (BulletList[i].liv is true)
            {
                if (EulaDis(self.x, self.y, BulletList[i].x, BulletList[i].y) < (BulletWeight + self.radius))
                {
                    BulletList[i].liv = false;
                    BulletCount--;
                }
            }
        }
        
    }
}


void mov(Item &self)
{
    self.y += ItemSpeed;
    if (self.y >= Height)
    {
        self.liv = false;
        Items_Count--;
    }
}

void mov(Magic &self)
{
    self.y += MagicSpeed;
    if (prob(87))
    {
        self.x += windy;
    }
    if (self.y >= Height  or self.x < 0  or self.x >= Width)
    {
        self.liv = false;
        Magic_Count--;
        return;
    }
    if (EulaDis(self.x, self.y, pl.base.x, pl.base.y) < (PlaneWeight + MagicWeight))
    {
        switch (self.dtype)
        {
            case MT_FIRE:
            {
                pl.Fire = FireTickMax;
            } break;
            
            case MT_BLIND:
            {
                pl.Blind = BlindTickMax;
            } break;
            
            default:
            {
                break;
            }
        }
        self.liv = false;
        Magic_Count--;
        return;
    }
}

void shoot(Player &self)
{
    CreateBullet(self);
    self.atkcool = PlayerAtkCool;
    StatisticalData.Shoot++;
}

void shoot(Enemy &self)
{
    CreateBullet(self);
    self.atkcool = EnemyAtkCool;
}

void hit(Player &self, float dmg)
{
    if (self.ShieldHP > 0)
    {
        dmg = dmg * (1 - self.ShieldHP / 1000) * ShieldReduceDamagePer;
        if (dmg < 0) dmg = 0.0001;
        self.ShieldHP -= (dmg <= 15 ? 15 : (float)(dmg >= 50 ? 50 : dmg));
        if (self.ShieldHP < 0)
        {
            self.ShieldHP = 0;
        }
    }
    self.base.hp -= dmg;
    if (self.base.hp < 0)
    {
        self.base.hp = 0;
    }
    StatisticalData.behit += dmg;
}

void hit(Enemy &self, float dmg, int8_t by)
{
    self.base.hp -= dmg;
    if (self.base.hp <= 0)
    {
        self.liv = false;
        Enemy_Count--;
        if (by is EB_PLAYER)
        {
            StatisticalData.q++;
            switch (self.dtype)
            {
            case ET_NORMAL:
            {
                score++;
            }
                break;
            case ET_BOSS:
            {
                score += 2;
            } break;
            
            case ET_BOMB:
            {
                score += 4;
            } break;
            
            case ET_CARRIER:
            {
                score += 8;
            } break;
            
            default:
                break;
            }
        }
        if (
            ((self.dtype is ET_BOSS) or (self.dtype is ET_CARRIER) or (self.dtype is ET_BOMB))
            and prob(GetItemPer)
        )
        {
            CreateItem(self);
        }
        elif (prob((100 - GetItemPer) / 8 + 1))
        {
            CreateItem(self);
        }
    }
}

void fire(Player &self)
{
    self.base.hp -= 4;
    if (self.ShieldHP > 0)
    {
        self.ShieldHP --;
    }
}

void __render_PlayerHud(Player &self)
{
    float per = self.base.hp / 1000.00f;
    constexpr int llen = 700;
    rectangle(100, 900, 100+llen, 910);
    if (self.Aid > 0)
    {
        setfillcolor(RGB(96, 255, 8));
    }else{
        setfillcolor(RGB(254, 37, 70));
    }
    fillrectangle(101, 901, 101+((llen-2) * per), 909);
    per = self.ShieldHP / 1000.00f;
    rectangle(100 + 100 + llen , 900, 100 + llen + 100+llen, 910);
    setfillcolor(RGB(235, 198, 20));
    fillrectangle(201 + llen, 901, 202 + llen +((llen-1) * per), 909);
}
void render(Player &self)
{
    setlinecolor(self.base.color);
    circle(self.base.x, self.base.y, PlaneWeight);
    line(self.base.x - PlaneWeight, self.base.y, self.base.x + PlaneWeight, self.base.y);
    line(self.base.x - PlaneWeight, self.base.y, self.base.x, self.base.y - (PlaneWeight/5));
    line(self.base.x, self.base.y - (PlaneWeight/5), self.base.x + PlaneWeight, self.base.y);
    __render_PlayerHud(self);
}

void render(Enemy &self)
{
    setlinecolor(self.base.color);
    if ((self.dtype is ET_BOMB) and (self.addon > 0))
    {
        setfillcolor(self.base.color);
        fillcircle(self.base.x, self.base.y, PlaneWeight);
        return;
    }
    circle(self.base.x, self.base.y, PlaneWeight);
    line(self.base.x - PlaneWeight, self.base.y, self.base.x + PlaneWeight, self.base.y);
    line(self.base.x - PlaneWeight, self.base.y, self.base.x, self.base.y + (PlaneWeight/5));
    line(self.base.x, self.base.y + (PlaneWeight/5), self.base.x + PlaneWeight, self.base.y);
}

void render(Bullet &self)
{
    setlinecolor(self.col);
    setfillcolor(self.col);
    fillcircle(self.x, self.y, BulletWeight);
}

void render(Meteorite &self)
{
    setlinecolor(RGB(170, 85, 0));
    setfillcolor(RGB(170, 85, 0));
    fillcircle(self.x, self.y, self.radius);
}

void render(Item &self)
{
    switch (self.dtype)
    {
    case IT_POWER:
    {
        setlinecolor(RGB(57, 11, 244));
        circle(self.x, self.y, ItemWeight);
        line(self.x, self.y + int(ItemWeight * 0.6667), self.x - 3, self.y - 3);
        line(self.x, self.y + int(ItemWeight * 0.6667), self.x + 3, self.y + 3);
        line(self.x, self.y + int(ItemWeight * 0.6667), self.x , self.y - 6);
    }
        break;
    
    case IT_AID:
    {
        setlinecolor(RGB(0, 255, 8));
        setfillcolor(RGB(0, 255, 8));
        circle(self.x, self.y, ItemWeight);
        fillrectangle(
            self.x - int(ItemWeight*0.67), self.y - int(ItemWeight*0.33), 
            self.x + int(ItemWeight*0.67), self.y + int(ItemWeight*0.33)
        );
        fillrectangle(
            self.x - int(ItemWeight*0.33), self.y - int(ItemWeight*0.67), 
            self.x + int(ItemWeight*0.33), self.y + int(ItemWeight*0.67)
        );
    } break;
    
    case IT_SHIELD:
    {
        setlinecolor(RGB(235 ,198, 20));
        setfillcolor(RGB(235, 198, 20));
        circle(self.x, self.y, ItemWeight);
        fillrectangle(
            self.x - int(ItemWeight*0.33), self.y - int(ItemWeight*0.67), 
            self.x + int(ItemWeight*0.33), self.y + int(ItemWeight*0.67)
        );
    }
    default:
        break;
    }
}

void render(Magic &self)
{
    setlinecolor(self.col);
    setfillcolor(self.col);
    fillcircle(self.x, self.y, MagicWeight);
}


void draw_bar(Enemy *carrier)
{
    if (carrier is nullptr)
    {
        return;
    }
    elif (carrier->dtype != ET_CARRIER)
    {
        return;
    }
    else if (carrier->base.hp > 0)
    {
        const float per = carrier->base.hp / CarrierHPMax;
        constexpr int wlen = Width - 400 - 400;
        // 400->(wlen)->400
        const int barlen = wlen * per;
        setlinecolor(RGB(0, 120, 120));
        setfillcolor(RGB(0, 120, 120));
        fillrectangle(400, 20, 400+wlen+2, 40);
        setlinecolor(RGB(255, 128, 128));
        setfillcolor(RGB(255, 128, 128));
        fillrectangle(401, 21, 400 + barlen, 38);
        seg::render::text(Width/2, 10, L"Boss Carrier", seg::render::Place::center);
    }
}

void Introduce()
{
    constexpr COLORREF bullet_1 = RGB(255, 0, 255);
    constexpr COLORREF bullet_2 = RGB(0xc5, 0xfb, 0x46);
    constexpr COLORREF bullet_3 = RGB(0xc5, 0xfb, 0x66);
    constexpr COLORREF enemy_normal = RGB(255,255,8);
    constexpr COLORREF enemy_boss = RGB(0xb3, 0x0e, 0xdc);
    constexpr COLORREF enemy_bomb = RGB(255,32,0);
    constexpr COLORREF enemy_carrier = RGB(0, 132, 132);
    const COLORREF enemycolor[4]{enemy_normal, enemy_boss, enemy_bomb, enemy_carrier};
    const wchar_t* enemyDesc[4] = {
        L"普通类型", L"高级类型, 注意避开它射击的粉色子弹",
        L"炸弹小飞机, 靠近玩家时会爆炸, 伤害高",
        L"游戏Boss, 能射出魔法子弹并给玩家debuff(燃烧、失明),会召唤其他敌对势力"
    };
    Bullet b1{100, 80, 1, 0, 0, true, EB_ENEMY, bullet_1};
    Bullet b2{200, 80, 1, 0, 0, true, EB_ENEMY, bullet_2};
    Bullet b3{300, 80, 1, 0, 0, true, EB_ENEMY, bullet_3};
    Enemy EE[4];
    for (int i = 0; i < 4; i++)
    {
        EE[i].base.x = i * PlaneWeight * 10 + 100;
        EE[i].base.y = 180;
        EE[i].base.color = enemycolor[i];
    }
    Meteorite st;
    st.x = 180;
    st.y = 400;
    st.radius = 100;
    Item its[3];
    for (int i = 0; i < 3; i++)
    {
        its[i].x = i * ItemWeight * 50 + 100;
        its[i].y = 560;
    }
    its[0].dtype = IT_POWER;
    its[1].dtype = IT_AID;
    its[2].dtype = IT_SHIELD;
    const wchar_t* itemDesc[3] = {
        L"力量药水, 让玩家可以多重射击", L"治疗药水, 加快生命值的回复", L"抗性提升, 抵消部分受到的伤害"
    };
    while (true)
    {
        Sleep(60);
        seg::render::fill(RGB(60, 60, 80));
        render(b1); render(b2); render(b3);
        outtextxy(100, 100, L"各种子弹。粉红色的子弹伤害高, 避免被它们射中! ");
        for (int i = 0; i < 4; i++)
        {
            render(EE[i]);
            seg::render::text(EE[i].base.x, EE[i].base.y+80, (TCHAR*)enemyDesc[i], seg::render::Place::center);
        }
        render(st);
        outtextxy(100, 520, L"陨石, 可对所有实体造成伤害(不包括item、magic)");
        for (int i = 0; i < 3; i++)
        {
            render(its[i]);
            seg::render::text(its[i].x, its[i].y + 40, (TCHAR*)itemDesc[i], seg::render::Place::center);
        }
        outtextxy(100, 630, L"WASD移动, J射击, P暂停");
        outtextxy(100, 660, L"按ESC退出");
        seg::render::flip();
        if (seg::Key::GetVirtualKey(VK_ESCAPE))
        {
            return;
        }
    }
}

#undef and
#undef is
#undef or
#undef elif